﻿<#
Syntax highlights a PowerShell script. See Convert-PS2Html for more information.

Version history:
1.2, Daniel Brännström, http://danielbrannstrom.wordpress.com:
Based code on pipelining for more flexibility along with some documentation cleanup.

1.1, Helge Klein, http://blogs.sepago.de/helge/: 
Loading the required assembly System.Web now. This was missing earlier.

1.0, Lee Holmes, http://www.leeholmes.com/blog/MorePowerShellSyntaxHighlighting.aspx: 
Initial version
#>

# Load required assemblies
[void] [System.Reflection.Assembly]::Load("System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")

$tokenColours = @{
    'Attribute' = '#FFADD8E6'
    'Command' = '#FF0000FF'
    'CommandArgument' = '#FF8A2BE2'
    'CommandParameter' = '#FF000080'
    'Comment' = '#FF006400'
    'GroupEnd' = '#FF000000'
    'GroupStart' = '#FF000000'
    'Keyword' = '#FF00008B'
    'LineContinuation' = '#FF000000'
    'LoopLabel' = '#FF00008B'
    'Member' = '#FF000000'
    'NewLine' = '#FF000000'
    'Number' = '#FF800080'
    'Operator' = '#FFA9A9A9'
    'Position' = '#FF000000'
    'StatementSeparator' = '#FF000000'
    'String' = '#FF8B0000'
    'Type' = '#FF008080'
    'Unknown' = '#FF000000'
    'Variable' = '#FFFF4500'
}

<# 
.SYNOPSIS
Internal helper function that is used to generate an HTML span and append it to HTML string builder.
#>
function Append-HtmlSpan($block, $tokenColor)
{    
	if (($tokenColor -eq 'NewLine') -or ($tokenColor -eq 'LineContinuation'))
	{
		if($tokenColor -eq 'LineContinuation')
		{
			$null = $codeBuilder.Append('`')
		}

		$null = $codeBuilder.Append("<br />`r`n")
	}
	else
	{
		$block = [System.Web.HttpUtility]::HtmlEncode($block)
		if (-not $block.Trim())
		{
			$block = $block.Replace(' ', '&nbsp;')
		}

		$htmlColor = $tokenColours[$tokenColor].ToString().Replace('#FF', '#')

		if($tokenColor -eq 'String')
		{
			$lines = $block -split "`r`n"
			$block = ""

			$multipleLines = $false
			foreach($line in $lines)
			{
				if($multipleLines)
				{
					$block += "<BR />`r`n"
				}

				$newText = $line.TrimStart()
				$newText = "&nbsp;" * ($line.Length - $newText.Length) + $newText
				$block += $newText
				$multipleLines = $true
			}
		}

		$null = $codeBuilder.Append("<span style='color:$htmlColor'>$block</span>")
	}
}

<#
.SYNOPSIS
Syntax highlights a PowerShell script

.DESCRIPTION
Supply Powershell script code as text. The output can be saved to file or to clipboard using standard Powershell pipes.
Dot source this file (Syntax: . "Highlight-Syntax.ps1") to load the commands into your current Powershell session.

.EXAMPLE
(Get-Content myscript.ps) -join "`r`n" | Convert-PS2Html | Out-File myscript.html

Read myscript.ps, convert to colored html and save the result to myscript.html. 

.EXAMPLE
$psISE.CurrentFile.Editor.SelectedText | Convert-PS2Html | Out-Clipboard

Get selected text in Powershell ISE, convert to colored html and save the result to the clipboard. 
Out-Clipboard is a command from the PSCX (Powershell community extensions) module. See 
http://pscx.codeplex.com/ for more information and download.
#>
function Convert-PS2Html
{     
    process 
    {
        # Get current pipeline object
        $text = $_
        
    	# Do syntax parsing.
    	$errors = $null
    	$tokens = [system.management.automation.psparser]::Tokenize($Text, [ref] $errors)

    	# Initialize HTML builder.
    	$codeBuilder = new-object system.text.stringbuilder

    	# Iterate over the tokens and set the colors appropriately.
    	$position = 0
    	foreach ($token in $tokens)
    	{
    		if ($position -lt $token.Start)
    		{
    			$block = $text.Substring($position, ($token.Start - $position))
    			$tokenColor = 'Unknown'
    			Append-HtmlSpan $block $tokenColor
    		}

    		$block = $text.Substring($token.Start, $token.Length)
    		$tokenColor = $token.Type.ToString()
    		Append-HtmlSpan $block $tokenColor

    		$position = $token.Start + $token.Length
    	}

    	# Build the entire syntax-highlighted script
    	$code = $codeBuilder.ToString()
    	
    	# Replace tabs with three blanks
    	$code = $code -replace "\t","   "

    	# Return result
    	$code
    }
}
